# -*- coding: utf-8 -*-

import os
import logging

from gzone.utils.media_processor import MediaProcessor

from flask import Blueprint, current_app, redirect, url_for, session, \
    request, render_template

from wechatpy.oauth import WeChatOAuth

from mpdev.wechat_token import APPID, APPSECRET
from mpdev.gzone_loader import current_user, current_room

from . import models

OAUTH_SCOPE = 'snsapi_userinfo'
OAUTH_STATE = '1'
REFRESH_USER_INFO_ENDPOINTS = ['gzone.index']

gzone = Blueprint(
    'gzone',
    __name__,
    url_prefix='/gzone',
    static_folder='static',
    template_folder='templates'
)

logger = logging.getLogger(__name__)


media_processor = MediaProcessor()


@gzone.record_once
def init_gzone(state):
    app, bp = state.app, state.blueprint
    bp.app = app

    app.config.setdefault('MEDIA_ROOT', None)
    app.config.setdefault('MEDIA_FOLDER', 'media')
    app.config.setdefault('MEDIA_URL', '/media/')
    app.config.setdefault('ALLOW_ADMIN_DELETION', True)
    app.config.setdefault('MOMENT_IMG_PRESERVE_ON_DELETE', False)
    app.config.setdefault('ALBUM_PHOTO_PRESERVE_ON_DELETE', False)
    app.config.setdefault('GROUP_FILE_PRESERVE_ON_DELETE', False)
    app.config.setdefault('INDEX_NOTICE_COUNT', 3)
    app.config.setdefault('MOMENT_PAGE_LENGTH', 15)
    app.config.setdefault('MOMENT_DELETION_TIMEUP', 300)
    app.config.setdefault('ALLOWED_UPLOAD_IMG_EXT', ('jpg', 'jpeg', 'png'))
    app.config.setdefault(
        'ALLOWED_UPLOAD_FILE_EXT',
        ('txt', 'pdf', 'doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx')
    )
    app.config.setdefault('GZONE_IMG_THUMBNAIL_SIZE', (128, 128))
    app.config.setdefault('USER_INFO_EXPIRATION', 24*60*60)

    bp.media_root = app.config.get('MEDIA_ROOT') or os.path.join(
        bp.root_path,
        app.config.get('MEDIA_FOLDER')
    )
    bp.media_url = app.config.get('MEDIA_URL')
    logger.debug('Set media_url for gzone: %s', bp.media_url)

    app.config['ALLOWED_UPLOAD_IMG_EXT'] = [
        ext.upper() for ext in app.config['ALLOWED_UPLOAD_IMG_EXT']
    ]
    app.config['ALLOWED_UPLOAD_FILE_EXT'] = [
        ext.upper() for ext in app.config['ALLOWED_UPLOAD_FILE_EXT']
    ]

    # for media url reverse generation
    # caution: this only sets one-way resolve(url generation)
    # so needs to config nginx to dispatch for media resource
    app.add_url_rule(
        bp.url_prefix + bp.media_url + '<path:filename>',
        '{}.{}'.format(bp.name, 'media')
    )

    media_processor.init_blueprint(bp)


from . import models
from . import views


@gzone.url_value_preprocessor
def pop_room_id(endpoint, url_params):
    if current_app.url_map.is_endpoint_expecting(endpoint, 'enc_room_id'):
        enc_room_id = url_params.pop('enc_room_id', None)
        ts = request.args.get('ts', type=str)
        roomid = current_app.serializer.dehash_room_id(str(enc_room_id), ts)
        if roomid is None:
            logger.warning(
                'Someone is requesting for invalid room. enc_room_id: %s. Code: %s',
                enc_room_id,
                403
            )
            # room_id错误的情况下，阻止访问上一次认证正确的room_id
            session.pop('enc_room_id', None)
            logger.info('CLEAR session: enc_roomid')
            session.pop('room_id', None)
            logger.info('CLEAR session: roomid')
        else:
            session['enc_room_id'] = enc_room_id
            logger.info('Set session: enc_roomid <- %s', session['enc_room_id'])
            session['room_id'] = roomid
            logger.info('Set session: room_id <- %s', session['room_id'])


@gzone.url_defaults
def push_room_id(endpoint, url_params):
    # 需要保证调用url_for的视图函数或者模板环境中设置了curr
    # 否则需要显式制定url_for()的enc_room_id参数(例如在mpdev.views.gzone_oauth2()中)
    if current_app.url_map.is_endpoint_expecting(endpoint, 'enc_room_id'):
        if 'enc_room_id' not in url_params:
            url_params['enc_room_id'] = current_room.enc_room_id


# 需要确保room_id的获取发生在user_binding之前
# 否则admin相关的认证会出错
@gzone.before_request
def validate_room():
    if current_room._get_current_object() is None:
        # todo: 非法roomid或者session过期的情况下错误处理
        # abort(403)
        json_data = dict(
            errors={'authentication': u'该群不存在或已过期,请联系管理员'},
            status_code=403
        )
        logger.warning(
            'Room validation failed: roomid=%s, enc_roomid=%s',
            session.get('room_id'),
            session.get('enc_room_id')
        )
        return render_template('invalid_room.html')

    logger.debug('Room validation passed: room_id=%s', current_room.id)

    # refresh room users
    if request.endpoint in REFRESH_USER_INFO_ENDPOINTS :
        logger.debug('BEGIN refreshing user info from redis.')
        users_r = current_app.redis_interface.get_room_users(current_room.id)
        models.Account.refresh_room_user(users_r)
        models.RoomAccount.refresh_room_user(users_r, current_room.id)


@gzone.before_request
def validate_user():
    if current_app.config.get('LOCAL_TEST'):
        session['openid'] = 'test_openid'
        session['nickname'] = 'test_user'

    # 所有对gzone的请求都需要先进行绑定
    if current_user.is_binding:
        logger.debug('User validation passed: openid=%s', current_user.openid)
        return

    if current_user.is_oauth_authenticated:
        logger.warning(
            'User tried to enter room %s.',
            current_room.id
        )
        # abort(403)
        return render_template('invalid_room.html')

    logger.info('Anonymouse user, redirect to OAuth authentication.')

    # for testing
    if current_app.config.get('SKIP_OAUTH2'):
        logger.warning('OAuth2 authentication SKIPPED!!')
        return

    ts = request.args.get('ts')
    authorize_url = url_for('gzone_oauth2', enc_room_id=current_room.enc_room_id, ts=ts, _external=True)
    authorize_url = WeChatOAuth(APPID, APPSECRET, authorize_url, OAUTH_SCOPE, OAUTH_STATE).authorize_url
    logger.info('Authorzie_url: %s roomid: %s', authorize_url, current_room.id)
    return redirect(authorize_url)


@gzone.before_request
def log_user_to_room():
    r_a = current_user.join(current_room._get_current_object())
    if r_a is None:
        logger.warning(
            'User tried to enter room %s. User openid: %s. Nickname: %s. Headimgurl: %s.',
            current_room.id,
            current_user.openid,
            current_user.nickname,
            current_user.headimgurl
        )
        # abort(403)
        return render_template('invalid_room.html')

    logger.debug(
        'User %s joined room %s RoomAccount: %s',
        current_user.openid,
        current_room.id,
        r_a.id
    )


@gzone.after_request
def record_response_header(res):
    logger.debug(
        'Response headers: %s',
        res.headers.to_list()
    )
    return res
